import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.KeyEvent;
import java.util.ArrayList;

public class GeneralEventProcessor extends DrawObjectEventProcessor
{
	// CALL METHODS OF THIS "GENERAL" EVENT PROCESSOR FIRST!
	// (sets some flags and objects required by the specialized DrawObjectEventProcessors!)

	/*
	Convention:
	
	Please check conditions in the following orders:
	
	KEYBOARD:
	
	if (MultiSelectionExisting)
	else if (DEFINED(SingleSelectedObject))
	else if (DEFINED(NearObject))
	else
	
	MOUSE:
	
	move
	drag
	left click
	right click
	left press
	right press
	no button
	
	MOUSE: key presses are to be checked in same if (MouseEventProcessor.[...]) line,
	after mouse checks.
	
	Update stuff, like resetting values, is to be done at very end of
	TryProcessUserMouseAction(), if possible.
	 
	*/

	private void CreateDrawObject(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor, ObjectStorage Objects)
	{
		Point PlacePos = DrawObject.AlignOnGrid(DrawObject.UnZoomPoint(MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow()))); // where to place new DrawObject

		if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_C))
		{
			if (!(Objects.IsObjectAtPoint(PlacePos)))
				Objects.AddObject("Node", PlacePos, null);
		}
		if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_F))
		{
			if (!(Objects.IsObjectAtPoint(PlacePos)))
				Objects.AddObject("CommentField", PlacePos, null);
		}
		if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_L))
		{
			if (!(Objects.IsObjectAtPoint(PlacePos)))
				Objects.AddObject("LearningNeuron", PlacePos, null);
		}
		if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_N))
		{
			if (!(Objects.IsObjectAtPoint(PlacePos)))
				Objects.AddObject("Neuron", PlacePos, null);
		}
		if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_S))
		{
			if (!(Objects.IsObjectAtPoint(PlacePos)))
				Objects.AddObject("SampleAndHold", PlacePos, null);
		}
		if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_T))
		{
			if (!(Objects.IsObjectAtPoint(PlacePos)))
				Objects.AddObject("TextField", PlacePos, null);
		}
		if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_V))
		{
			if (!(Objects.IsObjectAtPoint(PlacePos)))
				Objects.AddObject("VisualField", PlacePos, null);
		}
	}

	@Override
	public void TryProcessUserKeyboardAction(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor, ObjectStorage ObjectStorage, IOProgram IOProgram)
	{
		if (!(KeyboardEventsToBeProcessed(GUIElements)))
			return;

		// ****************************************************************
		// Del (delete DrawObject)
		if (KeyEventProcessor.IsKeyCodePressed(0x7f))
		{
			int DrawObjectCountOld = ObjectStorage.GetDrawObjectCount();

			if (MultiSelectionExisting)
			{
				ArrayList<DrawObject> MultiSelectionObjects = ObjectStorage.GetMultiSelectionObjects();

				ObjectStorage.DeleteObjects(MultiSelectionObjects);
				ObjectStorage.DeleteUnnecessaryObjects();
			}
			else if (DEFINED(NearObject))
			{
				if (NearObject.CanBeDeletedByUser())
				{
					ObjectStorage.DeleteObject(NearObject);
					ObjectStorage.DeleteUnnecessaryObjects(); // leak here for sure
				}
			}

			if (ObjectStorage.GetDrawObjectCount() != DrawObjectCountOld)
			{
				GUIElements.GetMainWindow().DoRedraw();
			}
		}
		// ****************************************************************
		if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_D) &&
			KeyEventProcessor.IsControlPressed())
		{
			int SpawnCount = 0;
			if (MultiSelectionExisting)
			{
				String SpawnCountString = Tools.ShowInputDialog("Duplicate how often?\r\nEnter positive number to spawn to the right, negative number to spawn down.");
				if (SpawnCountString != null && SpawnCountString.length() >= 1)
				{
					try
					{
						SpawnCount = Integer.parseInt(SpawnCountString);
					}
					catch (Exception e)
					{
						// probably NumberFormatException
					}
				}
				if (SpawnCount >= -1000000 && SpawnCount <= 1000000 && SpawnCount != 0)
				{
					ObjectStorage.SaveMultiSelectionToFile("./NCIDE_duplicate.dat", new LoadSaveDataEx(true, "", "", ZoomManager.GetZoomFactor()));

					Rectangle MultiSelectionRectangle = ObjectStorage.GetMultiSelectionRectangle(
						ObjectStorage.GetMultiSelectionObjects()
					);

					int GridSizex = 50; // TODO: don't hard code
					int GridSizey = 50; // TODO: don't hard code

					int Movex = MultiSelectionRectangle.width - (MultiSelectionRectangle.width % GridSizex);
					int Movey = MultiSelectionRectangle.height - (MultiSelectionRectangle.height % GridSizey);

					ObjectStorage.UnSetPartOfMultiSelectionForAllObjects();

					for (int s = 0; s < Math.abs(SpawnCount); s++)
					{
						int DrawObjectCountOld = ObjectStorage.GetDrawObjectCount();

						ObjectStorage.LoadAdditionalFromFile("./NCIDE_duplicate.dat");

						int DrawObjectCountNew = ObjectStorage.GetDrawObjectCount();

						for (int i = DrawObjectCountOld; i < DrawObjectCountNew; i++)
							ObjectStorage.SetPartOfMultiSelection(i);

						Point MoveAmount;

						if (SpawnCount >= 0)
							MoveAmount = new Point((int) ((s + 1) * Movex / GridSizex) * GridSizex + (s + 1) * GridSizex, 0); // round down to next Grid line at left
						else
							MoveAmount = new Point(0, (int) ((s + 1) * Movey / GridSizey) * GridSizey + (s + 1) * GridSizey); // round down to next Grid line above

						ObjectStorage.MoveMultiSelection(MoveAmount);
						ObjectStorage.UnSetPartOfMultiSelectionForAllObjects();
					}

					GUIElements.GetMainWindow().DoRedraw();
				}
			}
			else
			{
				GUIElements.GetMainWindow().GetTitleBarInfo().AddInfoText("Multi-select objects to duplicate!", true, false, false, false);
			}
		}
		// ****************************************************************
		// space (manually excite DrawObject(s))
		if (KeyEventProcessor.IsKeyCodePressed(KeyEvent.VK_SPACE))
		{
			if (IOProgram.IsExecuted())
			{
				if (MultiSelectionExisting)
				{
					ArrayList<DrawObject> MultiSelectionObjects = ObjectStorage.GetMultiSelectionObjects();

					ObjectStorage.ExciteMultipleObjects(MultiSelectionObjects);
				}
				else if (DEFINED(NearObject))
				{
					NearObject.AddExcitementForNextTick(1.0);
				}

				// don't redraw manually here, IO program execution causes this anyway!
				// GUIElements.GetMainWindow().DoRedraw();
			}
		}

		return;
	}

	@Override
	public void TryProcessUserMouseAction(MainWindow.GUIElements GUIElements, KeyEventProcessor KeyEventProcessor, MouseEventProcessor MouseEventProcessor, ObjectStorage ObjectStorage, IOProgram IOProgram)
	{
		if (!(MouseEventsToBeProcessed(GUIElements)))
			return;

		// update flags etc. automatically (could use some CPU time, but negligible here)
		MultiSelectionExisting = ObjectStorage.IsMultiSelectionExisting();

		// ****************************************************************
		if (MouseEventProcessor.IsMouseMoved())
		{
			NearObject = ObjectStorage.TrySelectNearObject(DrawObject.UnZoomPoint(MouseEventProcessor.GetMousePosPoint(GUIElements.GetMainWindow())));
		}
		// ****************************************************************
		if (MouseEventProcessor.IsLeftButtonClicked() &&
			KeyEventProcessor.AnyKeyPressed() &&
			NOT(MultiSelectionExisting))
		{
			CreateDrawObject(GUIElements, KeyEventProcessor, MouseEventProcessor, ObjectStorage);

			GUIElements.GetMainWindow().DoRedraw();

			MoveMousePosLast = UNDEFINED_POINT; // no button pressed, no moving!
		}
		// ****************************************************************
		if (MouseEventProcessor.NoButtonPressed())
		{
			if (NOT(MultiSelectionExisting) && DEFINED(NearObject))
			{
				if (NearObject != NearObjectLast)
				{
					ObjectStorage.UnHighlightAllObjects();

					NearObject.SetHighlighted(true);

					GUIElements.GetMainWindow().DoRedraw();
				}
			}
			else if (NOT(MultiSelectionExisting))
			{
				if (NearObject != NearObjectLast)
				{
					ObjectStorage.UnHighlightAllObjects();

					GUIElements.GetMainWindow().DoRedraw();
				}
			}

			MoveMousePosLast = UNDEFINED_POINT; // no button pressed, no moving!

			NearObjectLast = NearObject;
		}

		return;
	}
}
